package com.fourth.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fourth.dao.PermissionDao;
import com.fourth.dao.RoleDao;
import com.fourth.dao.RolePermissionDao;
import com.fourth.service.RedisService;
import com.fourth.service.RoleService;
import com.fourth.vo.EmpConditionVO;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ljx.entity.Permission;
import com.ljx.entity.Role;
import com.ljx.entity.RolePermission;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Mr.ma
 * @since 2023/2/27 13:56
 * CacheConfig 优先级最低的  这个表示在redis 创建一个文件夹 RoleService 在这个文件夹做缓存
 **/
@Service
@CacheConfig(cacheNames = "RoleService")
public class RoleServiceImpl implements RoleService {
    @Resource
    private RoleDao roleDao;

    @Resource
    private PermissionDao permissionDao;

    @Resource
    private RolePermissionDao rolePermissionDao;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private RedisService redisService;

    private final String Role_ZSET_KEY = "role_zSet";
    private final String Role_HASH_KEY = "role_hash";

    @Override
    @Cacheable(cacheNames="redis::role", key = "#p0.toString() + #p1.toString() + (#p2.getRoleName() != null ? #p2.getRoleName().toString() : '') + (#p2.getStatus() != null ? #p2.getStatus().toString() : '')")
    public IPage<Role> selectAllByCondition(Integer currentPage, Integer pageSize, Role condition) {
        IPage<Role> iPage = new Page<>(currentPage,pageSize);
        QueryWrapper<Role> wrapper = new QueryWrapper<>();
        if (condition.getRoleName() != null && condition.getRoleName().trim() != ""){
            wrapper.like("role_name",condition.getRoleName());
        }
        if (condition.getStatus() != null && condition.getStatus().trim() != ""){
            wrapper.eq("status", condition.getStatus());
        }

        iPage = roleDao.selectPage(iPage,wrapper);
        return iPage;
    }

    @Override
    public int insert(Role role) {
        role.setIsDeleted(0);
        int insert = roleDao.insert(role);
        return insert;
    }

    @Override
    public int updateRole(Role role) {
        int update = roleDao.updateById(role);
        return update;
    }

    @Override
    public int deleteById(String id) {
        int delete = roleDao.deleteById(id);
        return delete;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int upStatusFromRedis2DB() {
        List<Role> roles = redisService.getStateDataFromRedis();
        int count = 0;
        for (Role role : roles) {
            String id = role.getId();
            String status = role.getStatus();
            int i = roleDao.upStatus(id, status);
            count += i;
        }
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, List> selectPermission(String roleId) {
        List<Permission> permissions = permissionDao.selectList(null);
        List<Permission> firstMenus = firsts(permissions);

        List<Permission> permission = permissionDao.selectPermissionByRoleId(roleId);
        List<String> pIds = permission.stream().map(item -> item.getId()).collect(Collectors.toList());

        Map<String, List> map = new HashMap<>(64);
        map.put("firstMenus",firstMenus);
        map.put("permission",pIds);
        return map;
    }

    private List<Permission> firsts(List<Permission> permissions){
        //1.一级菜单
        List<Permission> firsts=new ArrayList<>();
        for (Permission permission:permissions){
            if(permission.getPid().equals("0")){
                firsts.add(permission);
            }
        }

        //2.查询一级菜单下的二级菜单
        for(Permission first:firsts){
            List<Permission> children=new ArrayList<>();
            for(Permission p:permissions){
                if(first.getId().equals(p.getPid())){
                    children.add(p);
                }
            }
            first.setChildren(children);
        }
        return firsts;
    }

    @Override
    public int updatePermission(String roleId, String[] menuids) {
        QueryWrapper<RolePermission> wrapper = new QueryWrapper();
        wrapper.eq("role_id",roleId);
        int delete = rolePermissionDao.delete(wrapper);

        RolePermission rolePermission = null;

        for (int i = 0; i < menuids.length; i++) {
            rolePermission = new RolePermission();
            rolePermission.setRoleId(roleId);
            rolePermission.setPermissionId(menuids[i]);
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            rolePermission.setGmtCreate(dateFormat.format(new Date()));
            rolePermission.setIsDeleted("0");
            rolePermissionDao.insert(rolePermission);
        }

        return menuids.length;
    }

    @Override
    public int upStatus(String id, String status) {
        return roleDao.upStatus(id, status);
    }

    @Override
    @Cacheable(key = "'RoleId' + #p0")
    public Role findById(String id) {
        return roleDao.selectById(id);
    }

    @Override
    @CachePut(key = "'RoleId' + #p0.getId()")
    public Role edit (Role role){
        role.setIsDeleted(0);
        int insert = roleDao.updateById(role);
        if (insert > 0){
            return role;
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteRoleById(String id){
        Role roleById = findById(id);
        int delete = roleDao.deleteById(id);
        if (delete > 0){
            // 如果失败可以使用消息队列尝试多次删除，此处省略
            Long remove = redisTemplate.opsForZSet().remove(Role_ZSET_KEY, roleById);
        }
        return delete;
    }

    /**
     * Cacheable 表示是否缓存空值，一般情况下是允许的。因为这涉及到缓存的三大问题：缓存穿透、缓存雪崩、缓存击穿。官方是怎么说的
     * @return
     */
    @Override
    public List<Role> selectAll() {
        QueryWrapper<Role> wrapper = new QueryWrapper<>();
        wrapper.orderByAsc("id");
        List<Role> roles = roleDao.selectList(wrapper);

        roles.stream().forEach(item -> {
            redisTemplate.opsForZSet().add(Role_ZSET_KEY,item.getRoleName()+":"+item.getStatus(),Double.parseDouble(item.getId()));
            redisTemplate.opsForHash().put(Role_HASH_KEY,item.getId(),JSON.toJSONString(item));
        });

        return roles;
    }

    @Override
    public Map<String,String> queryPage(Integer currentPage, Integer pageSize) {
        Long total = redisTemplate.opsForZSet().zCard(Role_ZSET_KEY);
        if (total <= 0) {
            selectAll();
        }

        Set<String> range = redisTemplate.opsForZSet().range(Role_ZSET_KEY, (currentPage-1)*pageSize, (currentPage-1)*pageSize + pageSize);

        List<Role> list = Arrays.asList(range.toArray(new Role[0]));

        String roleList = JSON.toJSONString(list);

        Map<String,String> map = new HashMap<>();
        map.put("total",total.toString());
        map.put("list",roleList);
        return map;
    }

    @Override
    public Map<String,String> queryPage(Integer currentPage, Integer pageSize,Role condition) {
        Long size = redisTemplate.opsForHash().size(Role_HASH_KEY);
        if (size <= 0) {
            selectAll();
        }
        List<String> ids = new ArrayList<>();
        Map<String,String> map = new HashMap<>();

        // WRONGTYPE Operation against a key holding the wrong kind of value
        try {
            ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
            ScanOptions scanOptions = ScanOptions.scanOptions().match(condition.getRoleName() != null ? "*" + condition.getRoleName() + "*" : "*" + ":" + condition.getStatus() != null ? condition.getStatus() : "*").build();
            // "*" + condition.getRoleName() + "*:*" + condition.getStatus()
            Cursor<ZSetOperations.TypedTuple<String>> cursor = zSetOperations
                    .scan(Role_ZSET_KEY, ScanOptions.NONE);

            while (cursor.hasNext()) {
                ZSetOperations.TypedTuple<String> item  = cursor.next();

                String score = item.getScore().toString();

                String[] split = score.split("\\.");

                ids.add(split[0]);
            }
            int total = ids.size();
            // (currentPage-1)*pageSize, (currentPage-1)*pageSize + pageSize
            List<String> subList = ids.stream().skip((currentPage-1)*pageSize).limit(pageSize).
                    collect(Collectors.toList());

            List<String> roleStrJsons = redisTemplate.opsForHash().multiGet(Role_HASH_KEY, subList);
            List<Role> roleJsons = roleStrJsons.stream().map(item -> JSON.parseObject(item, Role.class)).collect(Collectors.toList());
            String jsonString = JSON.toJSONString(roleJsons);
            map.put("total",total+"");
            map.put("list",jsonString);
            // 关闭cursor
            cursor.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }

}
